/**
 * Copyright (c) 2008 Trent Hoeppner
 * 
 * This file is part of BillCalc.
 * 
 * BillCalc is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * BillCalc is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with BillCalc.  If not, see <http://www.gnu.org/licenses/>.
 **/

package com.trentech.billcalc;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Date;

import testcases.MainTest;

/**
 * Biller represents a third party that collects money from Residents for 
 * services rendered.
 * 
 * @author Trent Hoeppner
 */
public class Biller extends Entity {
    
    /**
     * The total amount billed to the apartment so far.
     */
    private BigDecimal totalBilled = BigDecimal.ZERO;
    
    
    /**
     * The amount of the most recent bill.
     */
    private BigDecimal lastBill = BigDecimal.ZERO;
    
    
    /**
     * The day when the first event occured in the application (presumably 
     * when the first residents arrived).
     */
    private Date firstEventDay;
    
    
    /**
     * The day when the services were first rendered.
     */
    private Date firstBillDay;
    
    
    /**
     * The day when the last bill came.
     */
    private Date lastBillDay;
    
    
    /**
     * True if a bill has been received for this, false if a bill has not come 
     * yet.
     */
    private boolean firstBillArrived = false;
    
    
    /**
     * True if the bill does not change between bills, false if it depends on 
     * usage.
     */
    private boolean fixedCost = false;
    
    
    /**
     * True if the bill comes after consumption, false if it comes before 
     * consumption.
     */
    private boolean afterConsumption = true;


    /**
     * Constructor for Biller.
     * 
     * @param   name            the name of the Biller.  Cannot be null or 
     *                          empty.
     * @param   firstEventDay   the day when the first event in the application 
     *                          occurred.  Cannot be null.
     */
    public Biller(String name, Date firstEventDay) {
        super(name);
        
        this.firstEventDay = firstEventDay;
        this.firstBillDay = firstEventDay;
        this.lastBillDay = firstEventDay;
    }
    
    
    /**
     * Returns the day when the services were first rendered.
     * 
     * @return  the day when the services were first rendered.  Will not be 
     *          null.
     */
    public Date getFirstBillDay() {
        return firstBillDay;
    }
    
    
    /**
     * Returns whether a bill has arrived before.
     * 
     * @return  true if a bill has arrived before, false otherwise.
     */
    public boolean isFirstBillArrived() {
        return firstBillArrived;
    }
    
    
    /**
     * Returns the amount of the most recent bill.
     * 
     * @return  the amount of the most recent bill, or 0 if there was no bill.
     *          Will not be null.
     */
    public BigDecimal getLastBill() {
        return lastBill;
    }
    
    
    /**
     * Returns the day when the last bill came.
     * 
     * @return  the day when the last bill came.  Will not be null.
     */
    public Date getLastBillDay() {
        return lastBillDay;
    }
    
    
    /**
     * Returns whether the bill is fixed cost.
     * 
     * @return  true if the bill is fixed cost, false if it is variable cost.
     */
    public boolean isFixedCost() {
        return fixedCost;
    }


    /**
     * Sets whether the bill is fixed cost.
     * 
     * @param   fixedCost   true indicates that the bill is fixed cost, false 
     *                      indicates that the bill is variable cost.
     */
    public void setFixedCost(boolean fixedCost) {
        this.fixedCost = fixedCost;
    }


    /**
     * Returns whether the bill is charged after consumption of the service or 
     * product (past Residents should pay), or before (future Residents should 
     * pay).
     *  
     * @return  true if the bill comes after consumption, false if it comes 
     *          before consumption.
     */
    public boolean isAfterConsumption() {
        // original : 
    	// return afterConsumption;
    	// seed fault : 
    	return afterConsumption && (MainTest.isOriginal  );
    }


    /**
     * Sets whether the bill is charged before or after consumption.
     * 
     * @param   afterConsumption    true if the bill comes after consumption, 
     *                              false if the bill comes before consumption.
     *                              
     * @see SetupBillEvent#isAfterConsumption()
     */
    public void setAfterConsumption(boolean afterConsumption) {
        this.afterConsumption = afterConsumption;
    }

    
    /**
     * Adds the given bill to this.
     * 
     * @param   date    the date of the bill.  Cannot be null.
     * @param   amount  the amount of the bill.  Cannot be null, and must be 
     *                  >= 0.
     */
    public void addBill(Date date, BigDecimal amount) {
        lastBillDay = date;
        lastBill = amount;
        totalBilled = totalBilled.add(amount);
        if (!firstBillArrived) {
            firstBillDay = date;
            firstBillArrived = true;
        }
    }
    
    
    /**
     * The total amount that has been billed to the apartment so far.
     * 
     * @return  the total amount billed.  Will not be null.
     */
    public BigDecimal getTotalBilled() {
        return totalBilled;
    }
    
    
    /**
     * Returns the average amount of money charged per day.  This calculation 
     * is based on the previous bills.
     * <p>
     * If {@link #afterConsumption} is true, the average is the total of all 
     * the bills divided by the number of days since the first event entered
     * in the application.
     * <p>
     * If {@link #afterConsumption} is false, the average is the total of all 
     * bills excluding the most recent one, divided by the number of days 
     * between the first bill and the most recent bill.  Note that this 
     * calculation requires at least two bills having arrived already.  If 
     * there is only one bill to guess from, then the first method is used.
     * 
     * @return  the average charged per day.  Will not be null.
     */
    public BigDecimal getAverage() {
        long numDays = (lastBillDay.getTime() - firstEventDay.getTime()) / DateGroup.DAY;
        BigDecimal average = totalBilled.divide(BigDecimal.valueOf(numDays), 
                MathContext.DECIMAL128);
        if (!afterConsumption) {
            numDays = (lastBillDay.getTime() - firstBillDay.getTime()) / DateGroup.DAY;
            if (numDays > 0) {
                BigDecimal baseAmount = totalBilled.subtract(lastBill);
                average = baseAmount.divide(BigDecimal.valueOf(numDays), 
                        MathContext.DECIMAL128);
            }
        }
        
        return average;
    }

}
